/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package aprendizajePorRefuerzo.controlador;

import aprendizajePorRefuerzo.politica.Softmax;
import aprendizajePorRefuerzo.funcionQ.QImp;
import aprendizajePorRefuerzo.entorno.RespuestaEntorno;
import aprendizajePorRefuerzo.politica.EpsilonGreedy;
import aprendizajePorRefuerzo.estado.EstadoGrilla;
import aprendizajePorRefuerzo.estado.Estado;
import aprendizajePorRefuerzo.accion.Accion;
import aprendizajePorRefuerzo.accion.AccionGrilla;
import aprendizajePorRefuerzo.agente.Agente;
import aprendizajePorRefuerzo.entorno.EntornoGrilla;
import aprendizajePorRefuerzo.agente.AgenteImp;
import aprendizajePorRefuerzo.agente.AgenteQLearning;
import aprendizajePorRefuerzo.agente.AgenteSarsa;
import aprendizajePorRefuerzo.agente.FabricaAgentes;
import aprendizajePorRefuerzo.agente.FabricaPoliticas;
import aprendizajePorRefuerzo.entorno.Entorno;
import aprendizajePorRefuerzo.entorno.EntornoClimb;
import aprendizajePorRefuerzo.interfaz.FrmResultados;
import aprendizajePorRefuerzo.politica.Politica;
import java.util.ArrayList;
import java.util.List;

/**
 *
 * @author Fernando Sorzana
 */
public class Controlador {
    
    private FabricaAgentes fabricaAgentes;
    
    private FabricaPoliticas fabricaPoliticas;
    
    private AgenteImp agente;
    
    private EntornoGrilla entorno;
    
    private int numeroEpisodios;
    
    private int maximoNumeroPasos;
    
    private int episodiosEntreInformes;
    
    private List<Accion> accionesPosibles;

    public Controlador() {
        fabricaAgentes = new FabricaAgentes();
        fabricaPoliticas = new FabricaPoliticas();
    }

    public Agente getAgente() {
        return agente;
    }

    public void setAgente(AgenteImp agente) {
        this.agente = agente;
    }

    public int getNumeroEpisodios() {
        return numeroEpisodios;
    }

    public void setNumeroEpisodios(int numeroEpisodios) {
        this.numeroEpisodios = numeroEpisodios;
    }

    public int getMaximoNumeroPasos() {
        return maximoNumeroPasos;
    }

    public void setMaximoNumeroPasos(int maximoNumeroPasos) {
        this.maximoNumeroPasos = maximoNumeroPasos;
    }

    public int getEpisodiosEntreInformes() {
        return episodiosEntreInformes;
    }

    public void setEpisodiosEntreInformes(int episodiosEntreInformes) {
        this.episodiosEntreInformes = episodiosEntreInformes;
    }

    public Entorno getEntorno() {
        return entorno;
    }

    public void setEntorno(EntornoGrilla entorno) {
        this.entorno = entorno;
    }
    
    public List<Agente> listarAgentesPosibles(){
        return fabricaAgentes.listarAgentesPosibles();
    }
    
    public List<Politica> listarPoliticasPosibles(){
        return fabricaPoliticas.listarPoliticasPosibles();
    }
    
    //trazar la matriz q cada cierta cantidad de pasos.
    public void jugar(){
        EntornoGrilla entorno = new EntornoGrilla(4,4);
        entorno.agregarFinal(new EstadoGrilla(true,true,3,3));
        AgenteImp agente = new AgenteQLearning();
        agente.setAlfa(0.2);
        agente.setGamma(0.2);
        agente.setFuncionQ(new QImp());
        agente.setPolitica(new EpsilonGreedy(0.2));
        List<Accion> accionesPosibles = new ArrayList<>();
        accionesPosibles.add(new AccionGrilla(-1, 0, "subir"));
        accionesPosibles.add(new AccionGrilla(1, 0, "bajar"));
        accionesPosibles.add(new AccionGrilla(0, -1, "izquierda"));
        accionesPosibles.add(new AccionGrilla(0, 1, "derecha"));
        agente.setAccionesPosibles(accionesPosibles);
        for (int i = 0; i < 100; i++){
            int numeroPasos = 1;
            Estado estadoInicial = new EstadoGrilla(false,true,0,0);
//            System.out.println(((EstadoGrilla)estadoInicial).getFila() + "," + ((EstadoGrilla)estadoInicial).getColumna());
            Accion accionATomar = agente.tomarAccion(estadoInicial);
            RespuestaEntorno respuesta = entorno.devolverRespuesta(estadoInicial, accionATomar);
            agente.aprender(respuesta.getEstado(),respuesta.getReward());
//            System.out.println(((AccionGrilla)accionATomar).getNombre() + " > " + ((EstadoGrilla)respuesta.getEstado()).getFila() + "," + ((EstadoGrilla)respuesta.getEstado()).getColumna());
            while(!respuesta.getEstado().esFinal()){
                accionATomar = agente.tomarAccion(respuesta.getEstado());
                respuesta = entorno.devolverRespuesta(respuesta.getEstado(), accionATomar);
                agente.aprender(respuesta.getEstado(),respuesta.getReward());
//                System.out.println(((AccionGrilla)accionATomar).getNombre() + " > " + ((EstadoGrilla)respuesta.getEstado()).getFila() + "," + ((EstadoGrilla)respuesta.getEstado()).getColumna());
                numeroPasos++;
                if (numeroPasos > 300){
                    break;
                }
            }
            System.out.println(i + ";" + numeroPasos);
        }
        System.out.println(((QImp)agente.getFuncionQ()).imprimir());
    }
    
    public void jugarSarsa(){
        EntornoGrilla entorno = new EntornoGrilla(4,4);
        entorno.agregarFinal(new EstadoGrilla(true,true,3,3));
        AgenteImp agente = new AgenteSarsa();
        agente.setAlfa(0.2);
        agente.setGamma(0.2);
        agente.setFuncionQ(new QImp());
        agente.setPolitica(new EpsilonGreedy(0.2));
        List<Accion> accionesPosibles = new ArrayList<>();
        accionesPosibles.add(new AccionGrilla(-1, 0, "subir"));
        accionesPosibles.add(new AccionGrilla(1, 0, "bajar"));
        accionesPosibles.add(new AccionGrilla(0, -1, "izquierda"));
        accionesPosibles.add(new AccionGrilla(0, 1, "derecha"));
        agente.setAccionesPosibles(accionesPosibles);
        for (int i = 0; i < 1000; i++){
            int numeroPasos = 1;
            Estado estadoInicial = new EstadoGrilla(false,true,0,0);
//            System.out.println(((EstadoGrilla)estadoInicial).getFila() + "," + ((EstadoGrilla)estadoInicial).getColumna());
            Accion accionATomar = agente.tomarAccion(estadoInicial);
            RespuestaEntorno respuesta = entorno.devolverRespuesta(estadoInicial, accionATomar);
            agente.aprender(respuesta.getEstado(),respuesta.getReward());
//            System.out.println(((AccionGrilla)accionATomar).getNombre() + " > " + ((EstadoGrilla)respuesta.getEstado()).getFila() + "," + ((EstadoGrilla)respuesta.getEstado()).getColumna());
            while(!respuesta.getEstado().esFinal()){
                accionATomar = agente.tomarAccion(respuesta.getEstado());
                respuesta = entorno.devolverRespuesta(respuesta.getEstado(), accionATomar);
                agente.aprender(respuesta.getEstado(),respuesta.getReward());
//                System.out.println(((AccionGrilla)accionATomar).getNombre() + " > " + ((EstadoGrilla)respuesta.getEstado()).getFila() + "," + ((EstadoGrilla)respuesta.getEstado()).getColumna());
                numeroPasos++;
                if (numeroPasos > 300){
                    break;
                }
            }
            System.out.println(i + ";" + numeroPasos);
        }
        System.out.println(((QImp)agente.getFuncionQ()).imprimir());
    }

    public void jugar(FrmResultados res) {
        String buffer = "";
        int episodiosAcumulados = 0;
        agente.setFuncionQ(new QImp());
        agente.setAccionesPosibles(accionesPosibles);
        for (int i = 0; i < numeroEpisodios; i++){
            int numeroPasos = 1;
            Estado estadoInicial = entorno.getInicial();
//            System.out.println(((EstadoGrilla)estadoInicial).getFila() + "," + ((EstadoGrilla)estadoInicial).getColumna());
            Accion accionATomar = agente.tomarAccion(estadoInicial);
            RespuestaEntorno respuesta = entorno.devolverRespuesta(estadoInicial, accionATomar);
            agente.aprender(respuesta.getEstado(),respuesta.getReward());
//            System.out.println(((AccionGrilla)accionATomar).getNombre() + " > " + ((EstadoGrilla)respuesta.getEstado()).getFila() + "," + ((EstadoGrilla)respuesta.getEstado()).getColumna());
            while(!respuesta.getEstado().esFinal()){
                accionATomar = agente.tomarAccion(respuesta.getEstado());
                respuesta = entorno.devolverRespuesta(respuesta.getEstado(), accionATomar);
                agente.aprender(respuesta.getEstado(),respuesta.getReward());
//                System.out.println(((AccionGrilla)accionATomar).getNombre() + " > " + ((EstadoGrilla)respuesta.getEstado()).getFila() + "," + ((EstadoGrilla)respuesta.getEstado()).getColumna());
                numeroPasos++;
                if (numeroPasos > maximoNumeroPasos){
                    break;
                }
            }
            buffer += i + "," + numeroPasos + "\n";
            episodiosAcumulados++;
            if (episodiosAcumulados >= episodiosEntreInformes){
                episodiosAcumulados = 0;
                res.imprimir(buffer);
                buffer = "";
                res.imprimirQ(((QImp)agente.getFuncionQ()).imprimir());
            }
        }
    }
    
    public void jugarClimb(AgenteImp agente) {
        FrmResultados res = new FrmResultados();
        EntornoClimb entorno = new EntornoClimb();
        String buffer = "";
        int episodiosAcumulados = 0;
        agente.setFuncionQ(new QImp());
        agente.setAccionesPosibles(entorno.getAccionesPosibles());
        for (int i = 0; i < 1000; i++){
            int numeroPasos = 1;
            Estado estadoInicial = entorno.getInicial();
//            System.out.println(((EstadoGrilla)estadoInicial).getFila() + "," + ((EstadoGrilla)estadoInicial).getColumna());
            Accion accionATomar = agente.tomarAccion(estadoInicial);
            RespuestaEntorno respuesta = entorno.devolverRespuesta(estadoInicial, accionATomar);
            agente.aprender(respuesta.getEstado(),respuesta.getReward());
//            System.out.println(((AccionGrilla)accionATomar).getNombre() + " > " + ((EstadoGrilla)respuesta.getEstado()).getFila() + "," + ((EstadoGrilla)respuesta.getEstado()).getColumna());
            while(!respuesta.getEstado().esFinal()){
                accionATomar = agente.tomarAccion(respuesta.getEstado());
                respuesta = entorno.devolverRespuesta(respuesta.getEstado(), accionATomar);
                agente.aprender(respuesta.getEstado(),respuesta.getReward());
//                System.out.println(((AccionGrilla)accionATomar).getNombre() + " > " + ((EstadoGrilla)respuesta.getEstado()).getFila() + "," + ((EstadoGrilla)respuesta.getEstado()).getColumna());
                numeroPasos++;
                if (numeroPasos > 10){
                    break;
                }
            }
            buffer += i + "," + numeroPasos + "\n";
            episodiosAcumulados++;
            if (episodiosAcumulados >= 100){
                episodiosAcumulados = 0;
                res.imprimir(buffer);
                buffer = "";
                res.imprimirQ(((QImp)agente.getFuncionQ()).imprimir());
            }
        }
        res.setVisible(true);
    }
    
    public void jugarQLearningConSoftmax(){
        EntornoGrilla entorno = new EntornoGrilla(4,4);
        entorno.agregarFinal(new EstadoGrilla(true,true,3,3));
        AgenteImp agente = new AgenteQLearning();
        agente.setAlfa(0.2);
        agente.setGamma(0.2);
        agente.setFuncionQ(new QImp());
        agente.setPolitica(new Softmax(0.005));
        agente.setAccionesPosibles(accionesPosibles);
        for (int i = 0; i < 1000; i++){
            int numeroPasos = 1;
            Estado estadoInicial = new EstadoGrilla(false,true,0,0);
//            System.out.println(((EstadoGrilla)estadoInicial).getFila() + "," + ((EstadoGrilla)estadoInicial).getColumna());
            Accion accionATomar = agente.tomarAccion(estadoInicial);
            RespuestaEntorno respuesta = entorno.devolverRespuesta(estadoInicial, accionATomar);
            agente.aprender(respuesta.getEstado(),respuesta.getReward());
//            System.out.println(((AccionGrilla)accionATomar).getNombre() + " > " + ((EstadoGrilla)respuesta.getEstado()).getFila() + "," + ((EstadoGrilla)respuesta.getEstado()).getColumna());
            while(!respuesta.getEstado().esFinal()){
                accionATomar = agente.tomarAccion(respuesta.getEstado());
                respuesta = entorno.devolverRespuesta(respuesta.getEstado(), accionATomar);
                agente.aprender(respuesta.getEstado(),respuesta.getReward());
//                System.out.println(((AccionGrilla)accionATomar).getNombre() + " > " + ((EstadoGrilla)respuesta.getEstado()).getFila() + "," + ((EstadoGrilla)respuesta.getEstado()).getColumna());
                numeroPasos++;
                if (numeroPasos > 300){
                    break;
                }
            }
            System.out.println(i + ";" + numeroPasos);
        }
        System.out.println(((QImp)agente.getFuncionQ()).imprimir());
        
        
    }

    public void setMovimientos(boolean permiteDiagonal, boolean permiteQuieto) {
        accionesPosibles = new ArrayList<>();
        accionesPosibles.add(new AccionGrilla(-1, 0, "subir"));
        accionesPosibles.add(new AccionGrilla(1, 0, "bajar"));
        accionesPosibles.add(new AccionGrilla(0, -1, "izquierda"));
        accionesPosibles.add(new AccionGrilla(0, 1, "derecha"));
        if (permiteDiagonal){
            accionesPosibles.add(new AccionGrilla(-1, -1, "subir-izquierda"));
            accionesPosibles.add(new AccionGrilla(-1, 1, "subir-derecha"));
            accionesPosibles.add(new AccionGrilla(1, -1, "bajar-izquierda"));
            accionesPosibles.add(new AccionGrilla(1, 1, "bajar-derecha"));
        }
        if (permiteQuieto){
            accionesPosibles.add(new AccionGrilla(0, 0, "quieto"));
        }
    }

}
